<?php

namespace App\Console\Commands;

use App\Services\SwooleService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use Tymon\JWTAuth\Contracts\Providers\JWT as JWTContract;

class SwooleCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'swoole';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'swoole服务';

    private $serv;

    private $_bind_id;

    protected $provider;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(JWTContract $provider)
    {
        $this->provider = $provider;
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $this->serv = new \Swoole\WebSocket\Server("0.0.0.0", 9503);
        $this->serv->set(array(
            'worker_num' => 1,    //进程数
//            'heartbeat_check_interval' => 5,
        ));
        $this->serv->on('Start', array($this, 'onStart'));
        $this->serv->on('Handshake', array($this, 'handshake'));
        $this->serv->on('Open', array($this, 'onOpen'));
        $this->serv->on('Message', array($this, 'onMessage'));
        $this->serv->on('Close', array($this, 'onClose'));
        $this->serv->start();
    }

    /**
     * 服务启动
     * @param $serv
     */
    public function onStart($serv)
    {
        SwooleService::store();
        echo "启动成功\n";
    }

    public function handshake(\swoole_http_request $request, \swoole_http_response $response)
    {
//        //验证jwt
//        try {
//            $token  = $request->get['token'];
//            $result = $this->provider->decode($token);
//            if (time() > $result['exp']) {
//                $response->end();
//                return false;
//            }
//        } catch (\Exception $exception) {
//            $response->end();
//            return false;
//        }

        $secWebSocketKey = $request->header['sec-websocket-key'];
        $patten          = '#^[+/0-9A-Za-z]{21}[AQgw]==$#';
        if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) {
            $response->end();
            return false;
        }
        //echo $request->header['sec-websocket-key'];
        $key = base64_encode(sha1(
            $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
            true
        ));

        $headers = [
            'Upgrade'               => 'websocket',
            'Connection'            => 'Upgrade',
            'Sec-WebSocket-Accept'  => $key,
            'Sec-WebSocket-Version' => '13',
        ];
        if (isset($request->header['sec-websocket-protocol'])) {
            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
        }

        foreach ($headers as $key => $val) {
            $response->header($key, $val);
        }
        $response->status(101);
        $response->end();
    }

    /**
     * 连接成功
     *
     */
    public function onOpen($serv, $request)
    {
        echo "【{$request->fd}】连接成功\n";
    }

    /**
     * 接收消息
     *
     */
    public function onMessage($serv, $frame)
    {
        //格式化数据
        $data = json_decode($frame->data, true);
        //用户上线
        if (isset($data['type']) && isset($data['data'])) {
            if ($data['type'] == 'online') {
                $this->online($data['data'], $frame, $serv);
            } //发消息
            else if ($data['type'] == 'sendMessage') {
                $this->sendMessage($data['data'], $frame, $serv);
            } else {
                $serv->push($frame->fd, json_encode(['type' => 'ping', 'data' => '1']));
            }
        } else {
            $serv->push($frame->fd, json_encode(['type' => 'ping', 'data' => '2']));
        }
    }

    /**
     * 上线
     *
     * @param $data
     * @param $frame
     * @param $serv
     */
    public function online($data, $frame, $serv)
    {
        try {
            //绑定用户id
            $this->_bind_id[$frame->fd] = $data['id'];
            //通知用户
            foreach ($serv->connections as $v) {
                //告诉其他人用户上线
                if ($v != $frame->fd) {
                    $serv->push($v, json_encode(['type' => 'onlineStatus', 'data' => ['status' => true, 'id' => $data['id']]]));
                } else {
                    $info = SwooleService::online($data['id'], $this->_bind_id);
                    $serv->push($v, json_encode(['type' => 'setIni', 'data' => $info]));
                }
            }
        } catch (\Exception $exception) {

        }
    }

    /**
     * 下线
     */
    public function onClose($serv, $fd)
    {
        try {
            $id = $this->_bind_id[$fd];
            unset($this->_bind_id[$fd]);
            if (!in_array($id, $this->_bind_id)) {
                SwooleService::offline($id);
                foreach ($this->_bind_id as $key => $item) {
                    $serv->push($key, json_encode(['type' => 'onlineStatus', 'data' => ['status' => false, 'id' => $id]]));
                }
            }
        } catch (\Exception $exception) {

        }
        echo "客户端【{$fd}】已下线\n";
    }


    /**
     * 收发消息
     *
     * @param $data
     * @param $frame
     * @param $serv
     */
    public function sendMessage($data, $frame, $serv)
    {
        try {
            if ($data['to']['type'] == 'friend') {
                //私聊
                $to_online = in_array($data['to']['id'], $this->_bind_id);
                $result    = [
                    'type'      => $data['to']['type'],
                    'id'        => $to_online ? $data['mine']['id'] : $data['to']['id'],
                    'username'  => $to_online ? $data['mine']['username'] : $data['to']['username'],
                    "avatar"    => $to_online ? $data['mine']['avatar'] : $data['to']['avatar'],
                    "content"   => $to_online ? $data['mine']['content'] : '我不在，有事儿打电话',
                    "mine"      => false,
                    "fromid"    => time(),
                    "timestamp" => time()
                ];
                $id        = $to_online ? $data['to']['id'] : $data['mine']['id'];
                foreach ($this->_bind_id as $key => $item) {
                    if ($id == $item) {
                        $serv->push($key, json_encode(['type' => 'sendMessage', 'data' => $result]));
                    }
                }
            } else {
                //群聊
                $friend = Redis::get('admin_role_users');
                $friend = json_decode($friend, true);
                //发给群组其他人和超管
                foreach ($this->_bind_id as $key => $item) {
                    if (($data['mine']['id'] != $item) && (isset($friend[$data['to']['id']]['list'][$item]) or isset($friend[1]['list'][$item]))) {
                        $result = [
                            'type'      => $data['to']['type'],
                            'id'        => $data['to']['id'],
                            'username'  => $data['mine']['username'],
                            "avatar"    => $data['mine']['avatar'],
                            "content"   => $data['mine']['content'],
                            "mine"      => false,
                            "fromid"    => time(),
                            "timestamp" => time()
                        ];
                        $serv->push($key, json_encode(['type' => 'sendMessage', 'data' => $result]));
                    }
                }
            }
        } catch (\Exception $exception) {

        }
    }
}
